import { tools } from '@gislife/micro-message';
import config from '@/config';
import { imageToBase64 } from '@/utils/utils';
import pointIcon from '@/assets/marker_icons/default.png';
import * as WKT from '@terraformer/wkt';
import * as turf from '@turf/turf';
import CryptoJS from 'crypto-js';
import { deCryptoAES, Base64Util } from '@gislife/utils';
export const key = Base64Util.parse('OSEjOTVoc3VwKiYkMXpxNw==');

const LayerType = {
  point: {
    label: '点',
    value: 1,
  },
  polyline: {
    label: '线',
    value: 2,
  },
  polygon: {
    label: '面',
    value: 3,
  },
  detailPopover: {
    label: '详情弹窗',
    value: 4,
  },
  popPoint: {
    label: 'hover点',
    value: 5,
  },
};

export class MicroMessageUtils {
  microAppCode: any = config.microAppCode;
  private markerIds: any[] = []; // 所有点集合
  private polygonIds: any[] = []; // 所有面集合
  private polylineIds: any[] = []; // 所有线集合
  private detailPopoverIds: any[] = []; // 所有线集合
  private labelIds: any[] = []; // 所有标签集合
  private popPointIds: any[] = []; // 所有hover点集合
  private addPointAdsorbLineByPosIds: any[] = []; // 管线吸附点
  constructor(microAppCode?: any) {
    if (microAppCode) this.microAppCode = microAppCode;
  }

  // tools.sendCall
  sendCall(callback: (...args: any[]) => void, params = {}) {
    tools.sendCall(this.microAppCode, callback, params);
  }

  unlockWkt(wkt: string) {
    if (!wkt) return '';
    return [
      'LINESTRING',
      'MULTILINESTRING',
      'POINT',
      'MULTIPOINT',
      'POLYGON',
      'MULTIPOLYGON',
    ].some(type => wkt.startsWith(type))
      ? wkt
      : deCryptoAES(wkt, key);
  }

  // 加密
  enlockWkt(wkt: string) {
    const _wkt = CryptoJS.enc.Utf8.parse(wkt);
    const _key = CryptoJS.enc.Utf8.parse(key);
    const encrypt = CryptoJS.AES.encrypt(_wkt, _key, {
      mode: CryptoJS.mode.ECB,
      padding: CryptoJS.pad.Pkcs7,
    });

    const res = encrypt.toString();
    return res;
  }

  // wkt转geojson
  wktToGeoJSON(wkt: string) {
    return WKT.wktToGeoJSON(wkt);
  }

  // geojson转wkt
  geojsonToWKT(geometry: object) {
    return WKT.geojsonToWKT(geometry);
  }

  // wkt转pos-单个点
  wktToPos(wkt: string): [number, number] {
    return turf.getCoord(this.wktToGeoJSON(wkt)) as [number, number];
  }

  // pos转wkt-单个点
  posToWkt(pos: [number, number]): string {
    return this.geojsonToWKT(turf.point(pos).geometry);
  }

  //清除重复座标
  cleanRepeatCoords(coords: [number, number][]): [number, number][] {
    let data: any = coords.map(item => JSON.stringify(item));
    data = new Set(data);
    return [...data].map(item => JSON.parse(item));
  }

  // 定位
  locationByWkt(point: string, zoom = 15) {
    if (!point) return;
    const mapParams = {
      point,
      option: {
        zoom,
        duration: 1,
      },
    };
    this.sendCall((context, point, option) => {
      const mapboxFactory = context.mapApisRef.value.instance2d;
      mapboxFactory?.locationByWkt(point, option);
    }, mapParams);

    // connector.$send({
    //   target: this.microAppCode,
    //   type: 'locationByWkt',
    //   data: [point, { zoom, duration: 1000 }],
    // });
  }

  getLevelByWkt(wktString: string): number {
    const geojson = this.wktToGeoJSON(wktString);
    const bounds = geojson.coordinates.reduce(
      (acc: any, coord: any) => {
        acc[0] = acc[0] === null ? coord[0] : Math.min(acc[0], coord[0]);
        acc[1] = acc[1] === null ? coord[1] : Math.min(acc[1], coord[1]);
        acc[2] = acc[2] === null ? coord[0] : Math.max(acc[2], coord[0]);
        acc[3] = acc[3] === null ? coord[1] : Math.max(acc[3], coord[1]);
        return acc;
      },
      [null, null, null, null]
    );
    const distance = Math.max(bounds[2] - bounds[0], bounds[3] - bounds[1]);
    function getZoomLevel(distance: number) {
      return Math.round(14 - Math.log(distance / 0.01) / Math.LN2);
    }
    const zoomLevel = getZoomLevel(distance);
    console.log(zoomLevel);
    return zoomLevel > 15 ? 15 : zoomLevel;
  }

  // 定位
  locationByPos(point: [number, number], zoom = 15) {
    if (!point?.length) return;
    const wkt = WKT.geojsonToWKT(turf.point(point).geometry);

    const mapParams = {
      point: wkt,
      option: {
        zoom,
        duration: 1,
      },
    };
    this.sendCall((context, point, option) => {
      const mapboxFactory = context.mapApisRef.value.instance2d;
      mapboxFactory?.locationByWkt(point, option);
    }, mapParams);

    // connector.$send({
    //   target: this.microAppCode,
    //   type: 'locationByWkt',
    //   data: [wkt, { zoom, duration: 1000 }],
    // });
  }

  // 设置地图最大缩放等级
  setMaxZoom(zoom = 17) {
    this.sendCall(
      (context, zoom) => {
        const map = context.mapApisRef.value.instance2d.instance.map;
        map.setMaxZoom(zoom);
      },
      { zoom }
    );
  }

  // 根据wkt获取feature
  getFeatureByWkt(wkt: string, properties = {}, options = {}) {
    if (!wkt) return;
    return turf.feature(WKT.wktToGeoJSON(wkt), properties, options);
  }

  // 根据features获取featureCollection
  getFeatureCollectionByFeatures(features: any[]) {
    return turf.featureCollection(features);
  }

  // 根据多条管线获取中心点
  getCenterByLinesWkt(linesWkt: string[], returnGeojson = false) {
    const features = linesWkt
      .filter(wkt => wkt)
      .map(wkt => turf.feature(WKT.wktToGeoJSON(wkt)));
    if (!linesWkt.length) return;

    const center = turf.center(turf.featureCollection(features));
    if (returnGeojson) return turf.getCoords(center);
    return WKT.geojsonToWKT(center.geometry);
  }

  // 根据空间数据设置合适的缩放范围,可添加偏移
  setFitBoundsByWkts(
    wkts: string[],
    option?: {
      padding?:
        | number
        | { left?: number; right?: number; top?: number; bottom?: number };
      [key: string]: any;
    }
  ) {
    if (!option) {
      option = { padding: 20 };
    }
    const features = wkts
      .filter(wkt => wkt)
      .map(wkt => turf.feature(WKT.wktToGeoJSON(this.unlockWkt(wkt))));

    if (features.length) {
      const bbox = turf.bbox(turf.featureCollection(features));
      this.sendCall(
        (context, bbox, option) => {
          const map = context.mapApisRef.value.instance2d.instance.map;
          map.fitBounds(bbox, option);
        },
        { bbox, option }
      );
    }
  }

  // 根据多条管线获取合适的缩放层级
  getFitZoomByLinesWkt(linesWkt: string[]) {
    const features = linesWkt
      .filter(wkt => wkt)
      .map(wkt => turf.feature(WKT.wktToGeoJSON(wkt)));
    if (!linesWkt.length) return;

    const bbox = turf.bbox(turf.featureCollection(features));

    const from: any = turf.point([bbox[0], bbox[1]]);
    const to: any = turf.point([bbox[2], bbox[3]]);
    const distance = turf.distance(from, to, {
      units: 'meters',
    });
    return this.getZoomByDistance(distance);
  }

  // 线、面，获取合适的缩放层级
  getFitZoom(wkt: string) {
    if (!wkt) return;
    const bbox = turf.bbox(turf.feature(WKT.wktToGeoJSON(wkt)));

    const from: any = turf.point([bbox[0], bbox[1]]);
    const top: any = turf.point([bbox[2], bbox[3]]);
    const distance = turf.distance(from, top, {
      units: 'meters',
    });
    return this.getZoomByDistance(distance);
  }

  getZoomByDistance(distance: number = 0): number {
    const zoom = [
      50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 25000, 50000, 100000,
      200000, 500000, 1000000, 2000000, 5000000,
    ]; // 18级到2级。
    for (let i = 0, zoomLen = zoom.length; i < zoomLen; i++) {
      if (zoom[i] - distance > 0) {
        return 18 - i + 2;
      }
    }
    return 18;
  }

  // 清除点 线 面, 所以点线面的id不能重复
  clearMarker(id: any) {
    this.deleteId(id);
    this._clearMarker(id);
  }

  private deleteId(id: any, layerType?: number) {
    const deleteItem = (sourceArray: any[]) => {
      const index = sourceArray.findIndex(item => item === id);
      if (index !== -1) return sourceArray.splice(index, 1);
    };
    // 弹窗
    if (layerType === LayerType.detailPopover.value) {
      return deleteItem(this.detailPopoverIds);
    }

    // 点线面
    if (
      !deleteItem(this.markerIds) &&
      !deleteItem(this.polylineIds) &&
      !deleteItem(this.polygonIds) &&
      !deleteItem(this.labelIds)
    ) {
      return true;
    }
  }

  private _clearMarker(id: string) {
    const params = {
      id,
    };
    this.sendCall((context, id) => {
      const mapboxFactory = context.mapApisRef.value.instance2d;
      mapboxFactory?.removeGeoLayerById(id);
    }, params);

    // connector.$send({
    //   target: this.microAppCode,
    //   type: 'removeGeoLayerById',
    //   data: [id],
    // });
  }

  private _clearPopPoint(id: string) {
    const params = {
      id,
    };
    this.sendCall((context, id) => {
      const mapboxFactory = context.mapApisRef.value.instance2d;
      mapboxFactory?.removeCustomPointWithPopup(id);
    }, params);

    // connector.$send({
    //   target: this.microAppCode,
    //   type: 'removeCustomPointWithPopup',
    //   data: [id],
    // });
  }

  clearMarkers(ids: any[]) {
    ids.forEach(id => this.clearMarker(id));
  }

  clearAllPolygon() {
    this.polygonIds.forEach(id => this._clearMarker(id));
    this.polygonIds = [];
  }

  clearAllPolygonLine() {
    this.polylineIds.forEach(id => this._clearMarker(id));
    this.polylineIds = [];
  }

  clearPolygonLine(id: string) {
    const index = this.polylineIds.findIndex(polylineId => polylineId === id);
    if (index !== -1) {
      this.polylineIds.splice(index, 1);
      this._clearMarker(id);
    }
  }

  clearAllMarker() {
    this.markerIds.forEach(id => this._clearMarker(id));
    this.markerIds = [];
  }

  clearAllPopPoint() {
    this.popPointIds.forEach(id => this._clearPopPoint(id));
    this.popPointIds = [];
  }

  clearAllLabel() {
    this.labelIds.forEach(id => this._clearMarker(id));
    this.labelIds = [];
  }

  clearAllAddPointAdsorbLineByPos() {
    this.addPointAdsorbLineByPosIds.forEach(id => this._clearMarker(id));
    this.addPointAdsorbLineByPosIds = [];
  }

  clearAllLayer() {
    this.clearAllMarker();
    this.clearAllPolygon();
    this.clearAllPolygonLine();
    this.clearAllPopPoint();
    this.clearAllLabel();
    this.clearAllAddPointAdsorbLineByPos();
  }

  // 查找是否存在线id
  hasPolyline(id: string) {
    return this.polylineIds.includes(id);
  }

  // 画面
  drawPolygon(polygonWkt: any, id: any, params?: any) {
    if (!polygonWkt) return;
    const isExits = this.polygonIds.includes(id);
    if (isExits) return console.error('已存在 ' + id + ' 的面');
    const { color = 'red', zoom = 1 } = params || {};
    this.polygonIds.push(id);
    const mapParams = {
      params: {
        wkt: polygonWkt,
        id,
        color,
        zoom,
      },
    };
    this.sendCall((context, params) => {
      const mapboxFactory = context.mapApisRef.value.instance2d;
      mapboxFactory?.renderPolygon(params);
    }, mapParams);

    // connector.$send({
    //   target: this.microAppCode,
    //   type: 'renderPolygon',
    //   data: [
    //     {
    //       wkt: polygonWkt,
    //       id,
    //       color,
    //       zoom,
    //     },
    //   ],
    // });
  }

  // drawPolygonBySelect() {
  //   tools.sendCall(this.microAppCode, (context) => {
  //     const mapboxFactory = context.mapApisRef.value.instance2d;
  //     mapboxFactory?.renderPolygonBySelect((data:any)=>{
  //       context.connector.$send({
  //         target:"parent",
  //         type:"selectPolygonRes",
  //         data:data,
  //       })
  //     })
  //   });
  // }

  // hover点
  async drawPopPoint(pos: [number, number], id: any, params?: any) {
    if (!pos) return;
    const { html, img, style } = params || {};
    const image = await imageToBase64(img || pointIcon);
    const isExits = this.popPointIds.includes(id);
    if (isExits) return console.error('已存在 ' + id + ' 的标记点');
    this.popPointIds.push(id);
    const imgEle = document.createElement('img');
    imgEle.src = image as string;
    imgEle.setAttribute('style', style);

    const mapParams = {
      params: {
        id,
        position: pos,
        options: {
          html: imgEle.outerHTML,
          pop: { html, offset: [0, -50], closeButton: false },
        },
      },
    };
    this.sendCall((context, params) => {
      const mapboxFactory = context.mapApisRef.value.instance2d;
      mapboxFactory?.addCustomPointWithPopup(params);
    }, mapParams);

    // connector.$send({
    //   target: this.microAppCode,
    //   type: 'addCustomPointWithPopup',
    //   data: [
    //     id,
    //     pos,
    //     {
    //       html: imgEle.outerHTML,
    //       pop: { html, offset: [0, -50], closeButton: false },
    //     },
    //   ],
    // });
  }

  // 画点
  async drawPointMarker(pointWkt: any, id: any, params?: any) {
    if (!pointWkt) return;
    const { img, scale, pos = [], infos, text, textOffset } = params || {};
    const image = await imageToBase64(img || pointIcon);
    const isExits = this.markerIds.includes(id);
    if (isExits) return console.error('已存在 ' + id + ' 的标记点');
    this.markerIds.push(id);
    const mapParams = {
      params: {
        scale: scale ?? 1,
        wkt: pointWkt,
        id,
        url: image,
        pos,
        infos: infos ?? {},
        text,
        textOffset,
      },
    };
    this.sendCall((context, params) => {
      const mapboxFactory = context.mapApisRef.value.instance2d;
      mapboxFactory?.renderMarker(params);
    }, mapParams);
  }

  renderPolyline(lineWkt: any, id: any, params?: any) {
    const { color, width, hover, hoverColor, pop, infos } = params || {};
    if (!lineWkt) return;
    const isExits = this.polylineIds.includes(id);
    if (isExits) return console.error('已存在 ' + id + ' 的线');
    this.polylineIds.push(id);

    const mapParams = {
      params: {
        wkt: lineWkt,
        id,
        color: color ?? 'red',
        width: width ?? 5,
        hover: hover ?? true,
        hoverColor: hoverColor ?? '#0000ff',
        pop: pop ?? {},
        infos: infos ?? {},
      },
    };
    this.sendCall((context, params) => {
      const mapboxFactory = context.mapApisRef.value.instance2d;
      mapboxFactory?.renderPolyline(params);
    }, mapParams);

    // connector.$send({
    //   target: this.microAppCode,
    //   type: 'renderPolyline',
    //   data: [
    //     {
    //       wkt: lineWkt,
    //       id,
    //       color: color ?? 'red',
    //       width: width ?? 5,
    //       hover: hover ?? true,
    //       hoverColor: hoverColor ?? '#0000ff',
    //       pop: pop ?? {},
    //       infos: infos ?? {},
    //     },
    //   ],
    // });
  }

  // 根据多个点获取中心点
  getCenterByPointsWkt(pointsWkt: string[], returnGeojson = false) {
    if (!pointsWkt || pointsWkt.length < 2) return;
    const points: any = [];
    pointsWkt.forEach((wkt: string) => {
      if (wkt) {
        points.push(turf.feature(WKT.wktToGeoJSON(wkt)));
      }
    });
    const features = turf.featureCollection(points);
    const center = turf.center(features);
    if (returnGeojson) return turf.getCoords(center);
    return WKT.geojsonToWKT(center.geometry);
  }

  // 根据多个点获取合适的缩放
  getFitZoomByPointsWkt(pointsWkt: string[]) {
    if (!pointsWkt || pointsWkt.length < 2) return 7;
    const points: any = [];
    pointsWkt.forEach((wkt: string) => {
      if (wkt) {
        points.push(turf.feature(WKT.wktToGeoJSON(wkt)));
      }
    });
    const features = turf.featureCollection(points);
    const bbox = turf.bbox(features);
    const from: any = turf.point([bbox[0], bbox[1]]);
    const top: any = turf.point([bbox[2], bbox[3]]);
    const distance = turf.distance(from, top, {
      units: 'meters',
    });
    return this.getZoomByDistance(distance) ?? 7;
  }

  // 获取面中心点
  getCenterPointByPolygonWkt(polygonWkt: string, isGeoObj = false) {
    if (!polygonWkt) return;
    const polygonCoords = WKT.wktToGeoJSON(polygonWkt).coordinates;

    const polygon = turf.polygon(polygonCoords);
    const centerPoint = turf.center(polygon);
    if (isGeoObj) return centerPoint.geometry;
    return WKT.geojsonToWKT(centerPoint.geometry);
  }

  getCenterPointByLineStringWkt(lineStringWkt: string, isGeoObj = false) {
    if (!lineStringWkt) return;
    const lineString = WKT.wktToGeoJSON(lineStringWkt).coordinates;
    const line = turf.lineString(lineString);
    const centerPoint = turf.along(line, turf.length(line) / 2);
    if (isGeoObj) return centerPoint.geometry;
    return WKT.geojsonToWKT(centerPoint.geometry);
  }

  // 获取线中心点
  getCenterPointByMultiLineStringWkt(
    lineStringWkt: string,
    returnPos = false
  ): { wkt: any[]; type: string } {
    const result: { wkt: any[]; type: string } = { wkt: [], type: '' };
    if (!lineStringWkt) return result;
    const lineString = WKT.wktToGeoJSON(lineStringWkt).coordinates;

    // 判断是 multilinestring还是linestring
    if (lineStringWkt.includes('MULTILINESTRING')) {
      result.type = 'MULTILINESTRING';
      lineString.forEach((item: number[]) => {
        const line = turf.lineString(item as any[]);
        const centerPoint = turf.along(line, turf.length(line) / 2);
        result.wkt.push(
          returnPos
            ? centerPoint.geometry.coordinates
            : WKT.geojsonToWKT(centerPoint.geometry)
        );
      });
    } else {
      result.type = 'LINESTRING';
      const line = turf.lineString(lineString);
      const centerPoint = turf.along(line, turf.length(line) / 2);
      result.wkt.push(
        returnPos
          ? centerPoint.geometry.coordinates
          : WKT.geojsonToWKT(centerPoint.geometry)
      );
    }
    return result;
    // result 多线 [wkt,wkt,...] | [[lng,lat],[lng,lat],...]
    // result 单线 [wkt] | [[lng,lat]]
  }

  setVisible(data: any) {
    const mapParams = {
      params: data,
    };
    this.sendCall((context, params) => {
      const mapSubComponentsVisibleReactive =
        context.mapSubComponentsVisibleReactive;
      Object.assign(mapSubComponentsVisibleReactive, params);
    }, mapParams);

    // connector.$send({
    //   target: this.microAppCode,
    //   type: 'setVisible',
    //   data,
    // });
  }

  setShow(data: any) {
    const mapParams = {
      params: data,
    };
    this.sendCall((context, params) => {
      const mapSubComponentsShowReactive = context.mapSubComponentsShowReactive;
      Object.assign(mapSubComponentsShowReactive, params);
    }, mapParams);

    // connector.$send({
    //   target: this.microAppCode,
    //   type: 'setShow',
    //   data,
    // });
  }

  // 设置点线面显隐,match为id或自定义函数
  setLayerVisible(match: string | ((id: string) => boolean), visible = true) {
    if (!match) return;
    let ids: any[] = [];
    ids = ids.concat(
      this.markerIds,
      this.polygonIds,
      this.polylineIds,
      this.labelIds,
      this.popPointIds
    );
    let filterIds = [];
    if (typeof match === 'string') {
      const isExits = ids.includes(match);
      if (!isExits) return console.error('不存在 ' + match);
      filterIds = [match];
    } else {
      filterIds = ids.filter(id => match(id));
      if (!filterIds.length) return console.error('不存在要匹配的id');
    }
    filterIds.forEach(id => {
      const mapParams = {
        id,
        visible,
      };
      this.sendCall((context, id, visible) => {
        const mapboxFactory = context.mapApisRef.value.instance2d;
        mapboxFactory?.setLayerVisible(id, visible);
      }, mapParams);

      // connector.$send({
      //   target: this.microAppCode,
      //   type: 'setLayerVisible',
      //   data: [
      //     id, // 图层id
      //     visible, // true、false
      //   ],
      // });
    });
  }

  openDetailPopover(pointWkt: string, popId: any, data: any) {
    const { title, list, popCss, btns, html } = data;
    if (!pointWkt) return;
    this.detailPopoverIds.push(popId);

    const params = {
      config: {
        title: title ?? '',
        list: list ?? undefined,
        html: html ?? undefined,
        popCss,
        btns,
        id: popId,
      },
      position: WKT.wktToGeoJSON(pointWkt).coordinates,
    };
    this.sendCall((context, config, position) => {
      const mapboxFactory = context.mapApisRef.value.instance2d;
      context.popup = mapboxFactory.openDetailPopover(position, {
        ...config,
        btns: config.btns?.map((btn: any) => {
          return {
            ...btn,
            onClick: () => {
              context.$send({
                target: 'global',
                type: 'popBtnClick',
                data: btn,
              });
            },
          };
        }),
      });
    }, params);

    // connector.$send({
    //   target: this.microAppCode,
    //   type: 'openDetailPopover',
    //   data: [
    //     {
    //       title: title ?? '',
    //       list: list ?? undefined,
    //       html: html ?? undefined,
    //       popCss,
    //       btns,
    //       id: popId,
    //     },
    //     WKT.wktToGeoJSON(pointWkt).coordinates,
    //   ],
    // });
  }

  // 标签
  drawLabel(wkt: any, id: any, params?: any) {
    if (!wkt) return;
    const {
      text = 'text-label',
      fontsize = 16,
      color = 'red',
      translate = [0, 0],
    } = params || {};
    const isExits = this.labelIds.includes(id);
    if (isExits) return console.error('已存在 ' + id + ' 的标签');
    this.labelIds.push(id);
    const mapParams = {
      params: {
        wkt,
        id,
        text,
        fontsize,
        color,
        translate,
      },
    };
    this.sendCall((context, params) => {
      const mapboxFactory = context.mapApisRef.value.instance2d;
      mapboxFactory?.renderLabel(params);
    }, mapParams);
  }

  clearDetailPopover(id: any) {
    this.deleteId(id, LayerType.detailPopover.value);
    this._clearDetailPopover(id);
  }

  clearAllDetailPopover() {
    this.detailPopoverIds.forEach(id => this.clearDetailPopover(id));
  }

  private _clearDetailPopover(id: any) {
    const params = {
      id,
    };
    this.sendCall((context, id) => {
      const mapboxFactory = context.mapApisRef.value.instance2d;
      mapboxFactory?.instance.removePopById(id);
    }, params);

    // connector.$send({
    //   target: this.microAppCode,
    //   type: 'removeDetailPopover',
    //   data: [id],
    // });
  }
}
